package game;

import java.awt.Point;
import java.util.*;

import game.resources.ResourceType;

/**
 *	@(#)GameBoard.java
 *
 *	The <code>GameBoard</code> class represents the countryside.
 *	It manages the landtiles, hexfields and mountain ranges.
 *
 *	@author Grzegorz Kobiela
 *	@version 1.0 2008/4/24
 */
public final class GameBoard
extends GameObject
{
	/** The landtile objects. */
	private ArrayList<Landtile> landtiles;

	/** The mountain range objects. */
	private ArrayList<MountainRange> mountains;

	/** The hexfield objects. */
	private Hashtable<Point, Hexfield> hexfields;

	/**
	 *	Creates a new <code>GameObject</code> instance
	 *	based on the given landtiles.
	 *	@param tiles the landtiles
	 */
	public GameBoard(ArrayList<Landtile> tiles) {
		/* Add the landtiles */
		landtiles = new ArrayList<Landtile>();
		landtiles.addAll(tiles);

		/* Get all hexfields */
		hexfields = new Hashtable<Point, Hexfield>();
		for(Landtile tile: landtiles) {
			for(int i = 0; i < 61; i++) {
				Hexfield hex = tile.getHexfield(i);
				hexfields.put(hex.getLocation(), hex);
			}
		}

		/* Connect the hexfields */
		for(Point p: hexfields.keySet()) {
			Hexfield hex = hexfields.get(p);

			/* Top left neighbour */
			Hexfield neighbour = getHexfield(Hexfield.getNeighbour(p, Hexfield.NORTH_WEST));
			if(neighbour != null) {
				hex.setNeighbour(Hexfield.NORTH_WEST, neighbour);
				neighbour.setNeighbour(Hexfield.SOUTH_EAST, hex);
			}

			/* Neighbour above */
			neighbour = getHexfield(Hexfield.getNeighbour(p, Hexfield.NORTH));
			if(neighbour != null) {
				hex.setNeighbour(Hexfield.NORTH, neighbour);
				neighbour.setNeighbour(Hexfield.SOUTH, hex);
			}

			/* Top right neighbour */
			neighbour = getHexfield(Hexfield.getNeighbour(p, Hexfield.NORTH_EAST));
			if(neighbour != null) {
				hex.setNeighbour(Hexfield.NORTH_EAST, neighbour);
				neighbour.setNeighbour(Hexfield.SOUTH_WEST, hex);
			}
		}

		/* Find all mountain ranges */
		mountains = new ArrayList<MountainRange>();
		ArrayList<Hexfield> hexes = new ArrayList<Hexfield>();
		for(Hexfield hex: hexfields.values()) {
			if(!hex.isHill() || hex.isActive()) continue;
			hexes.clear();
			findMountainRange(hexes, hex);
			mountains.add(new MountainRange(hexes));
		}
		for(Hexfield hex: hexfields.values()) hex.deactivate();
	}

	/**
	 *	Searches for hill type hexes to add them to a mountain range.
	 *	@param hexes the hexfields already found
	 *	@param cur the current hexfield
	 */
	private void findMountainRange(ArrayList<Hexfield> hexes, Hexfield cur) {
		if(!cur.isHill() || cur.isActive()) return;
		hexes.add(cur);
		cur.activate();
		for(Hexfield hex: cur.getNeighbours()) if(hex != null) findMountainRange(hexes, hex);
	}

	/**
	 *	Returns the hexfield at the given location.
	 *	@param location the location
	 *	@return the hexfield at the given location
	 */
	public Hexfield getHexfield(Point location) {
		return hexfields.get(location);
	}

	/**
	 *	Returns all hexfields.
	 *	@return all hexfields
	 */
	public Collection<Hexfield> getHexfields() {
		return hexfields.values();
	}

	/**
	 *	Returns the mountain range that contains the given hexfield.
	 *	@param hexfield the hexfield
	 *	@return the mountain range
	 */
	public MountainRange getMountainRange(Hexfield hexfield) {
		for(MountainRange range: mountains) if(range.contains(hexfield)) return range;
		return null;
	}

	/**
	 *	Returns all the landtiles of the game board.
	 *	@return all the landtiles
	 */
	public ArrayList<Landtile> getLandtiles() {
		return landtiles;
	}

	/**
	 *	Determines the zone of control starting at the given hexfield
	 *	and recurring after the given number of steps.
	 *	@param hexes the list of hexes already found
	 *	@param cur the current hexfield
	 *	@param prev the previous hexfield
	 *	@param depth the number of steps to go
	 *	@param ref reference value (needed to count water correctly)
	 *	@param water provide true to count water hexes
	 */
	public void findZoneOfControl(ArrayList<Hexfield> hexes, Hexfield cur, Hexfield prev, int depth, int ref, boolean water) {
		if(depth < 0) return;
		boolean dejavu = hexes.contains(cur);
		if(cur.isWater() && !cur.isDisabled()) {
			if(!water || dejavu || (!prev.isWater() && ref != depth)) return;
			depth = 1;
		}
		if(!dejavu) hexes.add(cur);
		for(Hexfield hex: cur.getNeighbours()) if(hex != null && hex != prev) findZoneOfControl(hexes, hex, cur, depth - 1, ref, water);
	}

	/**
	 *	Returns the locations of the exploration chits.
	 *	Use this method once a game as it creates new
	 *	exploration chits every time it is called.
	 *	@return the locations of the exploration chits
	 */
	public ArrayList<Point> createExploration() {
		ArrayList<Point> exploration = new ArrayList<Point>();
		ArrayList<ResourceType> list = new ArrayList<ResourceType>();
		int n = landtiles.size() / 2;
		for(int i = 0; i < n; i++) {
			list.add(ResourceType.GRAIN);
			list.add(ResourceType.OLIVES);
			list.add(ResourceType.SHEEP);
			list.add(ResourceType.WINE);
		}
		Collections.shuffle(list);
		n = list.size();
		for(int i = 0; i < n; i++) {
			Hexfield hex = landtiles.get(i / 2).getExplorable(i % 2);
			hex.setExplorationChit(new ExplorationChit(list.get(i)));
			exploration.add(hex.getLocation());
		}
		return exploration;
	}
}